Dokumentation über die Erstellung der Visualisierungen zur Inkunabelproduktion der Stadt Bamberg, die in der Masterarbeit "Produktivität frühneuzeitlicher Druckorte am Beispiel Bamberg - Datenanalysen und Visualisierungen auf Basis des Gesamtkatalogs der Wiegendrucke" analysiert werden.
verwendete Technologien: Python 3 (Pakete: pandas, matplotlib, seaborn)
Datengrundlage: extrahierte Daten aus den XML-Werkkatalogen des Gesamtkatalog der Wiegendrucke zu finden in der CSV-Datei 2020-11-11_norm_all_final_editions.csv
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# laden der CSV-Daten
gw_ed = pd.read_csv('../05_Datenextraktion/Ausgaben_normalisiert/2020-11-11_norm_all_final_editions.csv', sep=';')
gw_ed
Die Spalte no_mp_bs enthält Angaben zum Publikationstyp:
no: normale Drucke
bs: Einblattdrucke
mp: mehrbändige Drucke
Die Spalte Sprache enthält die Angaben zur Sprache, wie sie aus dem GW-XML extrahiert wurden. Die Spalte norm_spr enthält die normalisierte Form der Sprachangaben mit den möglichen Werten ger, lat und ger lat. Gleiches gilt für die Angaben zum Druckdatum: Die Spalte Druckdatum beinhaltet die Angaben aus dem GW, norm_date beinhaltet die normalisierten Datumsformen. Das Vorgehen bei den Normalisierungen ist im Kapitel "Datenextraktion und Preprocessing" beschrieben.
Basisdaten: gw_ed: entspricht der Datei 2020-11-11_norm_all_final_editions.csv
Aus den Basisdaten gewonnene DataFrames:
gw_ed_no: enthält nur die Datensätze mit Publikationstyp no
gw_no_count: basiert auf gw_ed_no, enthält nur Spalten "norm_date" und neu aggregierte Spalte "Ausgaben" (Angaben dazu, wie viele normale Drucke pro Jahr hergestellt wurden)
gw_ed_offices: enthält eine zusätzliche Spalte mit angaben zu den Offizinen
gw_folio: enthält nur die Spalten "norm_date" und "Foliobögen"
gw_folio_grouped: gw_folio nach Jahren gruppiert
gw_folio_grouped_reset: gw_folio_grouped mit Index Reset
gw_folio_no_count: merge von gw_no_count und gw_folio_grouped
f_ed: enthält nur Spalten "norm_date" und neu aggregierte Spalte "Ausgaben" (Angaben dazu, wie viele Ausgaben pro Jahr insgesamt hergestellt wurden)
Dieser reduzierte Datensatz wird zum Vergleich der Ausgaben/Jahr mit den Foliobögen/Jahr verwendet, da es nur beim Publikationstyp no Angaben zu den Foliobögen gibt.
# erstellt einen neuen DataFrame, der nur die rows mit 'no' beinhaltet
gw_ed_no = gw_ed[gw_ed['no_mp_bs'] == 'no']
gw_ed_no
# erstellt ein Histogramm über die jährlich produzierten Ausgaben des Typs 'no'
plt.figure(figsize=(11,6))
plt.yticks(range(1,21,1))
plt.xticks(range(1460,1501,1),rotation=90)
plt.xlabel('Jahre')
plt.ylabel('Anzahl Ausgaben (no)')
plt.title('Ausgaben pro Jahr (nur no)')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed_no, x="norm_date", discrete=True, hue="no_mp_bs")
# speichert die Visualisierung
# plt.savefig('graphics/ausgaben_jahr_no.svg')
# erstellt ein Histogramm über die jährlich produzierten Ausgaben insgesamt
plt.figure(figsize=(11,6))
plt.yticks(range(1,21,1))
plt.xticks(range(1460,1501,1),rotation=90)
plt.xlabel('Jahre')
plt.ylabel('Ausgaben')
plt.title('Ausgaben pro Jahr (gesamt)')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed, x="norm_date", discrete=True)
# speichert die Visualisierung
# plt.savefig('graphics/ausgaben_jahr_gesamt.svg')
# erstellt ein Histogramm über die jährlich produzierten Ausgaben mit Hervorhebung der Sprachen
plt.figure(figsize=(11,6))
plt.yticks(range(1,21,1))
plt.xticks(range(1460,1501,1),rotation=90)
plt.xlabel('Jahre')
plt.ylabel('Anzahl Ausgaben')
plt.title('Ausgaben pro Jahr (Anteil der Sprachen)')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed, x="norm_date", discrete=True, hue="norm_spr", multiple="stack")
# speichert die Visualisierung
# plt.savefig('graphics/ausgaben_jahr_sprache.svg')
# erstellt ein Histogramm über die jährlich produzierten Ausgaben mit Hervorhebung der Publikationstypen
plt.figure(figsize=(11,6))
plt.yticks(range(1,21,1))
plt.xticks(range(1460,1501,1),rotation=90)
plt.xlabel('Jahre')
plt.ylabel('Anzahl Ausgaben')
plt.title('Ausgaben pro Jahr (Anteil der Publikationstypen)')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
hue_order = ['no', 'bs', 'mp']
sns.histplot(data=gw_ed, x="norm_date", discrete=True, hue="no_mp_bs", hue_order=hue_order, multiple="stack")
# speichert die Visualisierung
# plt.savefig('graphics/ausgaben_jahr_publtyp.svg')
# erstellt ein Histogramm über die jährlich produzierten Ausgaben mit Hervorhebung der Formate
plt.figure(figsize=(11,6))
plt.yticks(range(1,21,1))
plt.xticks(range(1460,1501,1),rotation=90)
plt.xlabel('Jahre')
plt.ylabel('Anzahl Ausgaben')
plt.title('Ausgaben pro Jahr (Anteil der Formate)')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
hue_order_format =['2','4','8','12', 'k.A.']
sns.histplot(data=gw_ed, x="norm_date", discrete=True, hue="Format", hue_order=hue_order_format, multiple="stack")
# speichert die Visualisierung
# plt.savefig('graphics/ausgaben_jahr_format.svg')
Um eine Visualisierung zu erhalten, welche die Anzahl der Ausgaben/Jahr nach Druckerwerkstätten gruppiert, muss zunächst ein neuer DataFrame mit einer zusätzlichen Spalte "Offizin" erstellt werden. Die Angaben zu den Druckern werden dabei gemäß dem folgenden Ersetzungs-Dictionary zu Offizinen zusammengefasst.
# Ersetzungs-Dictionary, fasst Drucker zu Offizinen zusammen
repl_offices = {
'Heinrich Petzensteiner' : 'J. Sensenschmidt/Petzensteiner',
'Johann Sensenschmidt' : 'J. Sensenschmidt/Petzensteiner',
'Johann Sensenschmidt und Heinrich Petzensteiner' : 'J. Sensenschmidt/Petzensteiner',
'Johann Pfeyl' : 'Pfeyl/L. Sensenschmidt/Petzensteiner',
'Lorenz Sensenschmidt' : 'Pfeyl/L. Sensenschmidt/Petzensteiner',
'Lorenz Sensenschmidt , Heinrich Petzensteiner und Johann Pfeyl': 'Pfeyl/L. Sensenschmidt/Petzensteiner',
'Lorenz Sensenschmidt , Johann Pfeyl und Heinrich Petzensteiner' : 'Pfeyl/L. Sensenschmidt/Petzensteiner',
'Heinrich Petzensteiner und Johann Pfeyl' : 'Pfeyl/L. Sensenschmidt/Petzensteiner',
'Marx Ayrer' : 'Ayrer/Bernecker',
'Marx Ayrer und Hans Bernecker': 'Ayrer/Bernecker',
'unbekannter Drucker mit Typen des Albrecht Pfister': 'unbekannt',
'unbekannter Drucker mit Typen von Johann Sensenschmidt': 'unbekannt',
'Albrecht Pfister': 'Albrecht Pfister',
'Hans Sporer': 'Hans Sporer'
}
# erstellt mithilfe einer Lambda-Funktion neuen DataFrame mit Spalte "Offizin"
gw_ed_offices = gw_ed
gw_ed_offices['Offizin'] = gw_ed_offices['Drucker'].apply(lambda printer: repl_offices[printer])
gw_ed_offices
# verschiebt Spalte "Offizin", sodass sie nach der Spalte "Drucker" erscheint
offices = gw_ed_offices.pop('Offizin')
gw_ed_offices.insert(10, 'Offizin', offices)
gw_ed_offices
# speichert den neuen DataFrame als CSV-Datei
# gw_ed_offices.to_csv('gw_ed_offices.csv', sep='|')
# erstellt ein Histogramm über die jährlich produzierten Ausgaben mit Hervorhebung der Offizinen
plt.figure(figsize=(11,6))
plt.yticks(range(1,21,1))
plt.xticks(range(1460,1501,1),rotation=90)
plt.xlabel('Jahre')
plt.ylabel('Anzahl Ausgaben')
plt.title('Ausgaben pro Jahr (nach Offizin)')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed_offices, x="norm_date", discrete=True, hue="Offizin", multiple="stack")
# speichert die Visualisierung
# plt.savefig('graphics/ausgaben_jahr_offizin.svg')
# erstellt einen Ausschnitt aus gw_ed, der nur die Spalten "norm_date" und "Foliobögen" enthält
gw_folio = gw_ed[['norm_date', 'Foliobögen']]
gw_folio
# gruppiert den DataFrame nach norm_date und bildet die Summe der Foliobögen, norm_date wird zum Index
gw_folio_grouped = pd.DataFrame(gw_folio.groupby(['norm_date'])['Foliobögen'].sum())
gw_folio_grouped
# speichert den neuen DataFrame als CSV-Datei
# gw_folio_grouped.to_csv('folioboegen_year.csv', sep=";")
# Index Reset: norm_date wird wieder als normaler Spaltenname und nicht mehr als Index betrachtet
gw_folio_grouped_reset = gw_folio_grouped.reset_index()
gw_folio_grouped_reset.head()
# erstellt eine Visualisierung der jährlich bedruckten Foliobögen
plt.figure(figsize=(11,6))
plt.yticks(range(0,550,50))
plt.xticks(range(1460,1501,1),rotation=90)
plt.xlabel('Jahre')
plt.ylabel('Anzahl Foliobögen')
plt.title('Foliobögen pro Jahr')
plt.bar(x = gw_folio_grouped_reset['norm_date'], height = gw_folio_grouped_reset['Foliobögen'])
# speichert die Visualisierung
# plt.savefig('graphics/folio_jahr.svg')
Um die Korrelation zwischen den Ausgaben/Jahr und den Foliobögen/Jahr analysieren zu können, muss zunächst eine neuer DataFrame erstellt werden, der Infos zur Anzahl der Ausgaben/Jahr und Foliobögen/Jahr umfasst. Die Basis dafür bildet gw_ed_no.
# zählt wie viele GW-Nummern pro Jahr vorkommen, ermittelt so die Anzahl der Ausgaben/Jahr
# die Spalte "GW-Nummer" wird in "Ausgaben" umbenannt
gw_no_count = pd.DataFrame(gw_ed_no.groupby(['norm_date'])['GW-Nummer'].count())
gw_no_count.rename(columns={'GW-Nummer':'Ausgaben'}, inplace=True)
gw_no_count
# zusammenfügen von gw_no_count und gw_folio_grouped
gw_folio_no_count = gw_no_count.merge(gw_folio_grouped, on='norm_date')
gw_folio_no_count
# Korrelation von Ausgaben/Jahr und Foliobögen/Jahr als Heatmap
plt.figure(figsize=(11,6))
plt.yticks(range(0,600,50))
plt.xticks(range(0,21,1))
plt.xlabel('Ausgaben/Jahr')
plt.ylabel('Foliobögen/Jahr')
plt.title('Korrelation: Anzahl Ausgaben und Foliobögen pro Jahr')
sns.histplot(gw_folio_no_count, x="Ausgaben", y="Foliobögen", cbar=True, binwidth=(1,50))
# speichert die Visualisierung
# plt.savefig('graphics/heatmap_folio_no.svg')
# berechnen des Bravais-Pearson-Korrelationskoeffizienten
folio_no_corr = gw_folio_no_count.corr()
folio_no_corr
# berechnet den Korrelationskoeffizienten nach Spearman
gw_folio_no_spearman = gw_folio_no_count.corr('spearman')
gw_folio_no_spearman
# Korrelation von Ausgaben/Jahr und Foliobögen/Jahr als Scatterplot
plt.figure(figsize=(11,6))
plt.yticks(range(0,600,50))
plt.xticks(range(0,21,1))
plt.xlabel('Ausgaben/Jahr')
plt.ylabel('Foliobögen/Jahr')
plt.title('Korrelation: Anzahl Ausgaben und Foliobögen pro Jahr')
sns.scatterplot(x = 'Ausgaben', y ='Foliobögen', data=gw_folio_no_count)
# speichert die Visualisierung
# plt.savefig('graphics/scatterplot_folio_no.svg')
# Visualisierung zur Verteilung der Sprachen insgesamt
plt.figure(figsize=(5,5))
plt.yticks(range(0,90,10))
plt.title('Sprache gesamt')
plt.xlabel('Spache')
plt.ylabel('Anzahl der Ausgaben')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed, x="norm_spr", discrete=True)
# speichert die Visualisierung
# plt.savefig('graphics/sprache_gesamt.svg')
plt.figure(figsize=(10,10))
plt.title('Sprachen pro Offizin')
plt.xlabel(' ')
plt.ylabel('Ausgaben')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed_offices, x="Offizin", hue="norm_spr", multiple="stack", shrink=.8)
plt.xticks(rotation = 20);
# speichert die Visualisierung
# plt.savefig('graphics/sprache_offizin.svg')
# Visualisierung zur Verteilung der Publikationstypen insgesamt
plt.figure(figsize=(5,5))
plt.yticks(range(0,90,10))
plt.title('Publikationstyp gesamt')
plt.xlabel('Publikationstyp')
plt.ylabel('Anzahl der Ausgaben')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed, x="no_mp_bs", discrete=True)
# speichert die Visualisierung
# plt.savefig('graphics/publtyp_gesamt.svg')
plt.figure(figsize=(10,10))
plt.title('Publikationstyp pro Offizin')
plt.xlabel(' ')
plt.ylabel('Ausgaben')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed_offices, x="Offizin", hue="no_mp_bs", multiple="stack", shrink=.8)
plt.xticks(rotation = 20);
# speichert die Visualisierung
# plt.savefig('graphics/publtyp_offizin.svg')
# Visualisierung zur Verteilung der Formate insgesamt
plt.figure(figsize=(5,5))
plt.yticks(range(0,90,10))
plt.title('Formate gesamt')
plt.xlabel('Format')
plt.ylabel('Anzahl der Ausgaben')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed, x="Format", discrete=True)
# speichert die Visualisierung
# plt.savefig('graphics/format_gesamt.svg')
plt.figure(figsize=(10,10))
plt.title('Formate pro Offizin')
plt.xlabel(' ')
plt.ylabel('Ausgaben')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_ed_offices, x="Offizin", hue="Format", multiple="stack", shrink=.8)
plt.xticks(rotation = 20);
# speichert die Visualisierung
plt.savefig('graphics/format_offizin.svg')
# erstellt neuen DataFrame, zählt Anzahl der GW-Nummern, um die jährliche Anzahl Ausgaben zu ermitteln
# Umbenennung der Spalte "GW-Nummer" in "Ausgaben"
f_ed = pd.DataFrame(gw_ed.groupby(['norm_date'])['GW-Nummer'].count())
f_ed.rename(columns={'GW-Nummer':'Ausgaben'}, inplace=True)
f_ed
# erstellt ein Histogramm über die Häufigkeitsverteilung der Jahresproduktion in Ausgaben
plt.figure(figsize=(10,5))
plt.yticks(range(1,8,1))
plt.xticks(range(1,21,1))
plt.xlabel('Ausgaben / Jahr')
plt.ylabel('Häufigkeit in Jahren')
plt.title('Jahresproduktion Bamberg - Häufigkeitsverteilung')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=f_ed, x="Ausgaben", discrete=True)
# speichert die Visualisierung
# plt.savefig('graphics/haeufigkeit_ausgaben_jahr.svg')
Lageparameter: Modus, Median, arithmetisches Mittel
Modus (Höhepunkt des Histogramms): 3
Am häufigsten wurden in Bamberg 3 Ausgaben pro Jahr produziert.
# berechnet die durchschnittliche Jahresproduktion (arithmetisches Mittel)
f_ed_mean = f_ed['Ausgaben'].mean()
f_ed_mean
Im Durchschnitt wurden in Bamberg jährlich 5.3 Ausgaben produziert.
# berechnet den Median
f_ed_median = f_ed['Ausgaben'].median()
f_ed_median
Der Median liegt bei 4.5 Ausgaben pro Jahr.
Streuungsparameter: Standardabweichung
# berechnet die Standardabweichung
f_ed_std = f_ed.loc[:,'Ausgaben'].std()
f_ed_std
Die Standardabweichung beträgt 4 Ausgaben.
# Jahresproduktion Bamberg - Häufigkeitsverteilung als Boxplot
plt.figure(figsize=(10,5))
plt.xticks(range(1,20,1))
plt.title('Boxplot - Ausgaben pro Jahr in Bamberg')
sns.boxplot(x=f_ed['Ausgaben'])
plt.xlabel('Ausgaben / Jahr')
# speichert die Visualisierung
# plt.savefig('graphics/haeufigkeit_ausgaben_jahr_boxplot.svg')
# ermittelt einige Parameter der Häufigkeitsverteilung
f_ed.describe()
# Visualisierung basiert auf gw_folio_grouped
gw_folio_grouped
# erstellt ein Histogramm über die Häufigkeitsverteilung der Jahresproduktion in Foliobögen
# Weil die Anzahl der Foliobögen sich in einem größeren Zahlenraum bewegt und daher (aufgrund der kleinen Datenmenge)
# die meisten Werte nur ein einziges Mal vorkommen, wird das Diagramm in bins einteilt (binwidth = 20).
# So erhält man eine aussagekräftigere Visualisierung als mit discrete=True, was dasselbe ist wie binwidth = 1.
plt.figure(figsize=(20,5))
plt.xticks(range(0,550,20))
plt.yticks(range(1,12,1))
plt.xlabel('Foliobögen / Jahr')
plt.ylabel('Häufigkeit in Jahren')
plt.title('Jahresproduktion Bamberg - Häufigkeitsverteilung Foliobögen (nur no)')
sns.set_context('poster')
sns.set_theme(style="whitegrid")
sns.histplot(data=gw_folio_grouped, x="Foliobögen", binwidth=20)
# speichert die Visualisierung
plt.savefig('graphics/haeufigkeit_folio_jahr.svg')
# ermittelt einige Parameter der Häufigkeitsverteilung
gw_folio_grouped.describe()
Der Modus liegt bei 0-20 Foliobögen.
Im Durchschnitt werden in Bamberg jährlich 98 Foliobögen bedruckt.
Der Median liegt bei 50,5 Foliobögen.
Die Standardabweichung beträgt 129,5 Foliobögen.
# Häufigkeitsverteilung der Foliobögen/Jahr als Boxplot
plt.figure(figsize=(20,5))
plt.xticks(range(0,550,20))
plt.title('Boxplot - Foliobögen pro Jahr in Bamberg')
sns.boxplot(x=gw_folio_grouped['Foliobögen'])
plt.xlabel('Foliobögen / Jahr')
# speichert die Visualisierung
# plt.savefig('graphics/haeufigkeit_folio_jahr_boxplot.svg')
# One Hot Encoding Sprache
spr_1hot = pd.get_dummies(gw_ed.norm_spr, prefix = 'Sprache', prefix_sep = '_')
# Spalten in folgende Reihenfolge bringen: ger, lat, ger lat
cols = spr_1hot.columns.tolist()
cols = ['Sprache_ger', 'Sprache_lat', 'Sprache_ger lat']
spr_1hot = spr_1hot[cols]
spr_1hot
# One Hot Encoding Format
format_1hot = pd.get_dummies(gw_ed.Format, prefix='Format')
# Spalten in folgende Reihenfolge bringen: 2, 4, 8, 12, k.A.
cols = format_1hot.columns.tolist()
cols = ['Format_2', 'Format_4', 'Format_8', 'Format_12', 'Format_k.A.']
format_1hot = format_1hot[cols]
format_1hot
# One Hot Encoding Publikationstyp
publtype_1hot = pd.get_dummies(gw_ed.no_mp_bs, prefix='Type')
# Spalten in folgende Reihenfolge bringen: no, bs, mp
cols = publtype_1hot.columns.tolist()
cols = ['Type_no', 'Type_bs', 'Type_mp']
publtype_1hot = publtype_1hot[cols]
publtype_1hot
# DataFrames spr_1hot, format_1hot, publtype_1hot zusammenfügen
oneHot = pd.concat([spr_1hot, publtype_1hot], axis=1)
oneHot = pd.concat([oneHot, format_1hot], axis=1)
oneHot
# Als Heatmap visualisieren
plt.figure(figsize=(15,10))
oneHot_corr = oneHot.corr()
oneHot_corr
sns.heatmap(oneHot_corr, annot=True, cmap='coolwarm',
vmin=-1, vmax=1, center= 0, square=True,
linewidths=1, linecolor='black')
# speichert die Visualisierung
# plt.savefig('graphics/heatmap_spr_format_publtyp.svg')
plt.savefig('graphics/heatmap_spr_format_publtyp.jpg', dpi=300)